import io.netty.util.internal.StringUtil;
import org.bouncycastle.util.encoders.Hex;
import org.hyperledger.fabric.sdk.Enrollment;
import org.hyperledger.fabric.sdk.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.Set;

/**
 * Created by xuguangyuansh on 2018/8/23.
 */
public class FabricUser implements User, Serializable {

    private static final long serialVersionUID = -5030375721479712836L;

    private static final Logger logger = LoggerFactory.getLogger(FabricUser.class);

    /** 名称 */
    private String name;
    /** 角色 */
    private Set<String> roles;
    /** 账户 */
    private String account;
    /** 从属联盟 */
    private String affiliation;
    /** 组织 */
    private String organization;
    /** 会员ID */
    private String mspId;
    /** 注册登记操作 */
    Enrollment enrollment = null;

    /** 存储配置对象 */
    private transient FabricStore keyValStore;
    private String keyValStoreName;

    public FabricUser(String name, String org, FabricStore store) {
        this.name = name;
        this.organization = org;
        this.keyValStore = store;
        this.keyValStoreName = toKeyValStoreName(this.name, org);

        String memberStr = keyValStore.getValue(keyValStoreName);
        if (null != memberStr) {
            saveState();
        } else {
            restoreState();
        }
    }

    /**
     * 设置账户信息并将用户状态更新至存储配置对象
     *
     * @param account 账户
     */
    public void setAccount(String account) {
        this.account = account;
        saveState();
    }

    @Override
    public String getAccount() {
        return account;
    }

    /**
     * 设置从属联盟信息并将用户状态更新至存储配置对象
     * @param affiliation 从属联盟
     */
    public void setAffiliation(String affiliation) {
        this.affiliation = affiliation;
        saveState();
    }

    @Override
    public String getAffiliation() {
        return affiliation;
    }

    @Override
    public Enrollment getEnrollment() {
        return enrollment;
    }

    /**
     * 设置会员 ID 信息并将用户状态更新至存储配置对象
     *
     * @param mspId 会员ID
     */
    public void setMspId(String mspId) {
        this.mspId = mspId;
        saveState();
    }

    @Override
    public String getMspId() {
        return mspId;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public Set<String> getRoles() {
        return roles;
    }

    /**
     * 设置注册登记操作信息并将用户状态更新至存储配置对象
     *
     * @param enrollment 注册登记操作
     */
    public void setEnrollment(Enrollment enrollment) {
        this.enrollment = enrollment;
        saveState();
    }

    /**
     * 确定这个名称是否已注册
     *
     * @return 是否
     */
    public boolean isRegistered() {
        return !StringUtil.isNullOrEmpty(name);
    }

    /**
     * 确定这个名字是否已经注册
     * @return
     */
    public boolean isEnrolled() {
        return this.enrollment != null;
    }

    /**
     * 将用户状态保存至存储配置对象
     */
    public void saveState() {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);
            oos.flush();
            keyValStore.setValue(keyValStoreName, Hex.toHexString(bos.toByteArray()));
            oos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 从键值存储中恢复该用户的状态（如果找到的话）。如果找不到，什么也不要做。
     *
     * @return 返回用户
     */
    private FabricUser restoreState() {
        String memberStr = keyValStore.getValue(keyValStoreName);
        if (null != memberStr) {
            // 用户在键值存储中被找到，因此恢复状态
            byte[] serialized = Hex.decode(memberStr);
            ByteArrayInputStream bis = new ByteArrayInputStream(serialized);
            try {
                ObjectInputStream ois = new ObjectInputStream(bis);
                FabricUser state = (FabricUser) ois.readObject();
                if (state != null) {
                    this.name = state.name;
                    this.roles = state.roles;
                    this.account = state.account;
                    this.affiliation = state.affiliation;
                    this.organization = state.organization;
                    this.enrollment = state.enrollment;
                    this.mspId = state.mspId;
                    return this;
                }
            } catch (Exception e) {
                throw new RuntimeException(String.format("Could not restore state of member %s", this.name), e);
            }
        }
        return null;
    }

    public static String toKeyValStoreName(String name, String org) {
        logger.debug("toKeyValStoreName = " + "user." + name + org);
        return "user." + name + org;
    }

    @Override
    public String toString() {
        return "FabricUser{" +
                "name='" + name + '\'' +
                ", roles=" + roles +
                ", account='" + account + '\'' +
                ", affiliation='" + affiliation + '\'' +
                ", organization='" + organization + '\'' +
                ", mspId='" + mspId + '\'' +
                ", enrollment=" + enrollment +
                ", keyValStore=" + keyValStore +
                ", keyValStoreName='" + keyValStoreName + '\'' +
                '}';
    }
}
